/**
 * @file csocket_request.cpp
 * @author Wang Bo (1276106221@qq.com)
 * @brief 连接处理相关
 * @version 0.1
 * @date 2021-03-02
 * 
 * @copyright Copyright (c) 2021
 * 
 */
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>

#include "global.h"
#include "csocket.h"
#include "memorypool.h"
#include "packet.h"
#include "spinlock.h"

void CSocket::waitReqHandle(pSConnection c)
{
    //收包开始
    ssize_t len = recvProcess(c, c->pRecvBuf, c->recvLen);
    if (len <= 0)
    {
        return ;
    }

    //如果能执行到这，说明收到了一定字节的数据
    if (c->curStat == pktStatus::HEADINIT) 
    {
        if (len == m_pktHeaderLen)
        {
            //收到完整包头
            waitReqHandleProcP1(c);
        }
        else
        {
            //包头不完整
            c->curStat = pktStatus::HEADRECV;
            c->pRecvBuf += len;
            c->recvLen -= len;
        }
    }
    else if (c->curStat == pktStatus::HEADRECV)
    {
        //正在接收包头的状态
        if (len == c->recvLen) //如果正好接收到了足够的包头
        {
            waitReqHandleProcP1(c);//进入下一阶段
        }
        else
        {
            //还没收完整，继续收包头
            c->pRecvBuf += len;
            c->recvLen -= len;
        }
    }
    else if (c->curStat == pktStatus::BODYINIT)
    {
        //包头刚好收完，开始收包体了
        if (len == c->recvLen)
        {
            //收到的包体长度等于期望的长度，太好了，完整的包
            waitReqHandleProcP2(c); //进入下一阶段处理包体
        }
        else
        {
            c->curStat = pktStatus::BODYRECV; //标记为已经在收包体了，没收完整
            c->pRecvBuf += len;
            c->recvLen -= len;
        }
    }
    else if (c->curStat == pktStatus::BODYRECV)
    {
        //继续接受包体
        if (len == c->recvLen)
        {
            waitReqHandleProcP2(c);
        }
        else
        {
            c->pRecvBuf += len;
            c->recvLen -= len;
        }
    }
    return ;
}

ssize_t CSocket::recvProcess(pSConnection c, char* buf, ssize_t len)
{
    ssize_t nRead;
    nRead = recv(c->fd, buf, len, 0);
    if (nRead == 0)
    {
        //返回值为0的情况一般是客户端关闭了（4次挥手），服务器正常关闭socket即可
        closeConnection(c);
        return -1;
    }

    if (nRead < 0) //有错误发生
    {
        if (errno == EAGAIN || errno == EWOULDBLOCK) //不应该出现
        {
            LOGSTDERR(errno, "CSocket::recvProcess() unexpected error");
            return -1;
        }

        if (errno == EINTR) //不应该出现
        {
            LOGSTDERR(errno, "CSocket::recvProcess() EINTR unexpected");
            return -1;
        }

        if (errno == ECONNRESET) //客户端非正常关闭
        {
            //TODO：暂不处理，后续增加
        }
        
        //不是上面定义的任何一种错误，关闭连接
        LOGSTDERR(errno, "CSocket::recvProcess() unexpected error");
        closeConnection(c);
        return -1;
    }
    //代码到这里表示收到了有效数据
    //且数据已经读到了buf中去
    return nRead; 
}

//包头处理阶段P1
void CSocket::waitReqHandleProcP1(pSConnection c)
{
    CMemory *pMemory = CMemory::GetInstance();
    pPKT_HEADER pHeader;
    pHeader = (pPKT_HEADER)c->dataHeadInfo;

    uint16_t pktlen = ntohs(pHeader->pktLen); //网络序转本机序

    if ((pktlen < m_pktHeaderLen) || pktlen > (MAX_PKT_LEN - 1000)) //判断错误包或者恶意包
    {
        c->curStat = pktStatus::HEADINIT; //重置收包阶段
        c->pRecvBuf = c->dataHeadInfo;
        c->recvLen = m_pktHeaderLen;
    }
    else //收到合法的包头了，继续处理
    {
        char* pTempBuf = (char *)pMemory->AllocMemory(m_pktHeaderLen + pktlen, false);
        c->ifNew = true; //设置为使用了new，后面关闭连接的时候需要释放内存
        c->pNewMem = pTempBuf; //指向分配好的内存空间
        //1.写消息头内容
        pMSG_HEADER pMsg = (pMSG_HEADER)pTempBuf;
        pMsg->pConn = c;
        pMsg->curSeq = c->iCurSeq;
        //2.填写包头内容
        pTempBuf += m_msgHeaderLen; //指针跳过消息头，指向包头
        memcpy(pTempBuf, pHeader, m_pktHeaderLen); //写入包头

        if (pktlen == m_pktHeaderLen) //只有包头无包体
        {
            //允许这种情况，继续下一阶段
            waitReqHandleProcP2(c);
        }
        else
        {
            c->curStat = pktStatus::BODYINIT;
            c->pRecvBuf = pTempBuf + m_pktHeaderLen; //指针移动到包头后面
            c->recvLen = pktlen - m_pktHeaderLen; //待接收的数据-已写入的包头数据
        }
    }
    return ;
}

void CSocket::waitReqHandleProcP2(pSConnection c)
{
    int mqCnt = 0;
    inMsgRecvQueue(c->pNewMem, mqCnt);
    g_threadpool.Call(mqCnt);

    c->ifNew = false; //pNewMem中的内存交给inMsgRecvQueue中处理了，不需要由连接池释放了
    //重置连接属性，等待下一次收包
    c->pNewMem = nullptr;
    c->curStat = pktStatus::HEADINIT;
    c->pRecvBuf = c->dataHeadInfo;
    c->recvLen = m_pktHeaderLen;
    return ;
}

void CSocket::inMsgRecvQueue(char* buf, int &mqCnt)
{
    LockGuard guard(m_mqSpinLock);
    m_MsgRecvQueue.push_back(buf); //压入一个元素入队
    //m_MsgRecvQueue.size() //list本身也有size，下面的可以也可以不必获取
    ++m_recvMsgQueueSize; //队列元素+1
    mqCnt = m_recvMsgQueueSize;

    //TODO其他功能待完善
    return ;
}

char* CSocket::outMsgRecvQueue()
{
    LockGuard guard(m_mqSpinLock);
    if (m_MsgRecvQueue.empty())
    {
        return nullptr;
    }
    char* tempbuf = m_MsgRecvQueue.front(); //返回第一个元素
    m_MsgRecvQueue.pop_front(); 
    --m_recvMsgQueueSize; //元素数量-1
    return tempbuf;
}

void CSocket::threadRecvProcFunc(char* pMsgBuf)
{
    //TODO线程处理包的逻辑待添加
    return ;
}